namespace sim {
  export type BpardPin = string
  export type PinStyle = "male" | "female" | "solder" | "croc";
  export interface BBloc {
    type: '垫板',
    row: string,
    col: string,
    xOffset?: number,
    yOffset?: number,
    style?: PinStyle
  }



  export interface BoardLoc {
    type: '装饰板',
    pin: BoardLoc
  }

  export type Loc = BBloc | BoardLoc

  export function mkRange(a: number, b: number): number[] {
    let res: number[] = []
    for (; a < b; a++) res.push(a)
    return res
  }

  export class EventBus {
    private 队列s: { [index: string]: EventQueue } = {}
    private 通知ID: number
    private notifyOneID: number
    private 日程安排ID: number
    private 空闲事件ID: number
    private lastEventValue: string | number
    private lastEventTimestampUs: number
    private 背景处理标志: boolean = false

    public nextNotifyEvent = 1024

    constructor(private runtime: Runtime, private valueToArgs?: EventValueToActionArgs) {
      this.日程安排ID = 15 // 设备ID调度程序
      this.空闲事件ID = 2 // 设备调度程序空闲
    }

    /**
     * name
     */
    public set背景处理标志() {
      this.背景处理标志 = false
    }

    /**
     * name
     */
    public set通知ID(通知ID: number, notifyOneID: number) {
      this.通知ID = 通知ID
      this.notifyOneID = notifyOneID
    }

    /**
     * set空闲
     */
    public set空闲(日程安排ID: number, 空闲事件ID: number) {
      this.日程安排ID = 日程安排ID
      this.空闲事件ID = 空闲事件ID
    }

    private start(id: EventIDType, evid: EventIDType, backgound: boolean, create = false) {
      let key = `${backgound ? 'back' : 'fore'}:${id}:${evid}`
      if (!this.队列s[key] && create) this.队列s[key] = new EventQueue(this.runtime, this.valueToArgs)
      return this.队列s[key]
    }

    listen(id: EventIDType, evid: EventIDType, handler: RefAction) {
      if (id == this.日程安排ID && evid == this.空闲事件ID) {
        this.runtime.start闲置()
      }
      let q = this.start(id, evid, this.背景处理标志, true)
      if (this.背景处理标志) {
        q.addHandler(handler)
      } else {
        q.setHandler(handler)
      }
      this.背景处理标志 = false
    }

    removerBackgroundHandler(handler: RefAction) {
      Object.keys(this.队列s).forEach((key: string) => {
        if (key.startsWith("back:")) {
          this.队列s[key].removeHandler(handler)
        }
      })
    }

    private get队列s(id: EventIDType, evid: EventIDType, bg: boolean) {
      let ret: EventQueue[] = [this.start(0, 0, bg)]
      if (id == 0 && evid == 0) {
        return ret
      }
      if (evid) ret.push(this.start(0, evid, bg))
      if (id) ret.push(this.start(id, 0, bg))
      if (id && evid) ret.push(this.start(id, evid, bg))
      return ret
    }

    队列(id: EventIDType, evid: EventIDType, value: EventIDType = 0) {
      if (runtime.暂停断点) return
      const notifyOne: boolean = !!(this.通知ID && this.notifyOneID && id == this.notifyOneID)
      if (notifyOne) id = this.通知ID
      let queues = this.get队列s(id, evid, true).concat(this.get队列s(id, evid, false))
      this.lastEventValue = evid
      this.lastEventTimestampUs = U.perfNowUs()
      Promise.all(queues).then(res => {
        res.forEach(q => {
          if (q) return q.push(value, notifyOne)
          else return Promise.resolve()
        })
      })
    }

    队列空闲() {
      if (this.日程安排ID && this.空闲事件ID) {
        this.队列(this.日程安排ID, this.空闲事件ID)
      }
    }

    等待(id: number | string, evid: number | string, cb: (value?: any) => void) {
      let q = this.start(id, evid, false, true)
      q.addAwaiter(cb)
    }

    getLastEventValue() {
      return this.lastEventValue
    }

    getLastEventTime() {
      return 0xfffffff & (this.lastEventTimestampUs - runtime.startTimeUs)
    }

  }

  export interface AnimationOptions {
    interval: number;
    frame: () => boolean;
    whenDone?: (cancelled: boolean) => void
    setTimeoutHandle?: number
  }

  export class Animation队列 {
    private queue: AnimationOptions[] = []
    private process: () => void

    constructor(private runtime: Runtime) {
      this.process = () => {
        let top = this.queue[0]
        if (!top) return
        if (this.runtime.dead) return
        runtime = this.runtime
        let res = top.frame()
        runtime.queueDisplayUpdate()
        runtime.msybeUpdateDisplay()
        if (res === false) {
          this.queue.shift()
          // 如果队列有东西就开始处理
          if (this.queue[0]) {
            this.queue[0].setTimeoutHandle = setTimeout(this.process, this.queue[0].interval)
          }
          if (top.whenDone) top.whenDone(false)

        } else {
          top.setTimeoutHandle = setTimeout(this.process, top.interval)
        }
      }
    }

    public 取消All() {
      let q = this.queue
      for (let a of q) {
        a.whenDone ? a.whenDone(true) : ''
        if (a.setTimeoutHandle) {
          clearTimeout(a.setTimeoutHandle)
        }
      }
    }

    public 取消当前() {
      let top = this.queue[0]
      if (top) {
        this.queue.shift()
        if (top.whenDone) top.whenDone(true)
        if (top.setTimeoutHandle) clearTimeout(top.setTimeoutHandle)

      }
    }

    public enqueue(anim: AnimationOptions) {
      if (!anim.whenDone) anim.whenDone = () => { }
      this.queue.push(anim)
      if (this.queue.length == 1) this.process()
    }

    public executeAsync(anim: AnimationOptions) {
      U.assert(!anim.whenDone)
      return new Promise<boolean>((res) => {
        anim.whenDone = res
        this.enqueue(anim)
      })
    }
  }

  export namespace AudioContextManager {
    let _频率 = 0
    let _context: AudioContext
    let _vco: OscillatorNode // 振荡器
    let _vca: GainNode

    let _mute = false // muto audio

    let audio: HTMLAudioElement

    function context(): AudioContext {
      if (!_context) _context = freshContext()
      return _context
    }

    function freshContext(): AudioContext {
      return new AudioContext
    }

    export function mute(mute: boolean) {
      _mute = mute
      stopAll()
      const ctx = context()
      if (!mute && ctx && ctx.state === 'suspended') ctx.resume()
    }

    function stopTone() {
      if (_vca) _vca.gain.value = 0
      _频率 = 0
      if (audio) audio.pause()
    }

    export function stopAll() {
      stopTone()
      muteAllchannels()
    }

    export function stop() {
      stopTone()
    }

    export function 频率(): number {
      return _频率
    }

    const waveForms: OscillatorType[] = [null, 'triangle', 'sawtooth', 'sine']
    let noiseBuffer: AudioBuffer

    let squareBuffer: AudioBuffer[] = []

    function getNoiseBuffer() {
      if (!noiseBuffer) {
        const bufferSize = 100000
        noiseBuffer = context().createBuffer(1, bufferSize, context().sampleRate);
        const output = noiseBuffer.getChannelData(0)

        let x = 0xf01ba80;
        for (let i = 0; i < bufferSize; i++) {
          x ^= x << 13;
          x ^= x >> 17
          x ^= x << 5
          output[i] = ((x & 1023) / 512.0) - 1.0
        }
      }
      return noiseBuffer
    }

    function getSquareBuffer(param: number) {
      if (!squareBuffer[param]) {
        const bufferSize = 1024
        const buf = context().createBuffer(1, bufferSize, context().sampleRate)
        const output = buf.getChannelData(0)
        for (let i = 0; i < bufferSize; i++) {
          output[i] = i < (param / 100 * bufferSize) ? 1 : -1
        }
        squareBuffer[param] = buf
      }
      return squareBuffer[param]
    }

    function getGenerator(waveFormIdx: number, hz: number): OscillatorNode | AudioBufferSourceNode | null {
      let form = waveForms[waveFormIdx]
      if (form) {
        let src = context().createOscillator()
        src.type = form
        src.frequency.value = hz
        return src
      }
      let buffer: AudioBuffer
      if (waveFormIdx == 5) {
        buffer = getNoiseBuffer()
      } else if (11 <= waveFormIdx && waveFormIdx <= 15) {
        buffer = getSquareBuffer((waveFormIdx - 10) * 10)
      } else {
        return null
      }

      let node = context().createBufferSource()
      node.buffer = buffer
      node.loop = true
      if (waveFormIdx != 5) {
        node.playbackRate.value = hz / (context().sampleRate / 1024)
      }
      return node
    }

    const channels: Channel[] = []

    /**
     * 渠道
     */
    class Channel {
      发生器: OscillatorNode | AudioBufferSourceNode | null = null
      gain: GainNode | null = null
      mute() {
        if (this.发生器) {
          this.发生器.stop()
          this.发生器.disconnect()
        }
        if (this.gain) {
          this.gain.disconnect()
        }
        this.gain = null
        this.发生器 = null
      }

      remove() {
        const idx = channels.indexOf(this)
        if (idx >= 0) channels.splice(idx, 1)
        this.mute()
      }
    }

    let instrStopId = 1

    export function muteAllchannels() {
      instrStopId++
      while (channels.length) {
        channels[0].remove()
      }
    }

    export function queuePlayInstructions(when: number, b: RefBuffer) {
      const prevStop = instrStopId
      new Promise((res) => {
        setTimeout(res, when)
      }).then(() => {
        if (prevStop != instrStopId) {
          return Promise.resolve()
        }
        return playInstructionsAsync(b)
      }).catch(e => console.log(e))
    }

    export function playInstructionsAsync(b: RefBuffer) {
      const prevStop = instrStopId
      let ctx = context()

      let idx = 0
      let ch = new Channel()
      let currWave = -1
      let currFreq = -1
      let timeOff = 0

      if(channels.length > 5) {
        channels[0].remove()
      }
      channels.push(ch)

      // 方波被认为比其他声音更响亮，所以把它缩小一点，使它不那么刺耳

      const scaleVol = (n: number, isSqWave?:boolean)=> (n/1024)/2*(isSqWave?.7:1)
      const finish = ()=> {
        ch.mute()
        timeOff = 0
        currWave = -1
        currFreq = -1
      }

      const loopAsync = (): Promise<void> => {
        if(idx>= b.data.length || !b.data[idx]) {
          return Promise.resolve().then(finish)
        }
        const soundWaveIdx = b.data[idx]
        
      }
    }


  }
}